﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace Lilium.LExpress
{
    public static class ExpressionMonad
    {
        public static ExpressionMonad<T> Lambda<T>(Expression<Func<T>> lambda)
        {
            return new ExpressionMonad<T>(lambda.Body);
        }
    }

    public class ExpressionMonad<T>
    {
        public ExpressionMonad(Expression expression)
        {
            if (expression == null) throw new ArgumentNullException("expression");

            if (!typeof(T).IsAssignableFrom(expression.Type))
                throw new NotImplementedException("Expr type {0} is not assignable from expression type {1}".Format(typeof(T), expression.Type));

            Expression = expression;
        }

        public T Eval()
        {
            return (T)Expression.Eval();
        }

        public ExpressionMonad<T> Select(Expression<Func<T, T>> mapper)
        {
            if (mapper == null) throw new ArgumentNullException("mapper");

            if (mapper.IsId())
                return this;
            else
                return new ExpressionMonad<T>(Expression.Invoke(mapper, Expression));
        }

        public ExpressionMonad<T1> Select<T1>(Expression<Func<T, T1>> mapper)
        {
            return new ExpressionMonad<T1>(Expression.Invoke(mapper, Expression));
        }

        private readonly Expression Expression;

        private IDictionary<Expression, Expression> CreateSubstitutionDictionary(LambdaExpression mapper)
        {
            var result = new Dictionary<Expression, Expression>();
            result.Add(mapper.Parameters[0], Expression);
            return result;
        }
    }
}
